package com.xinz.protocal;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;

import com.xinz.protocal.client.RemoteInvoker;
import com.xinz.serialization.IndexMap;
import com.xinz.serialization.StreamContext;

/**
 * 表示一个srmi连接会话
 * Session对象保持着连接两端连接的上下文<br>
 * 这些上下文包括：
 * <li>序列化类型：用于快速传输对象,不需要每次传输都传输对象类型信息</li>
 * <li>方法列表缓存：用于快速调用远程方法,不需要每次传输都方法信息</li>
 * <li>用户自己定义的k,v对象（tags）</li>
 * <li>接口对应的本地实现</li>
 * 通过注册接口注册共享接口的本地实现
 * session对象会自动的根据现在的调用内容添加类型和接口上下文
 * 但是必须
 * @author xingyun
 */
public class Session {

	/**
	 * 远程方法列表缓存
	 */
	IndexMap<Object, Method> remoteMethodTable = new IndexMap<Object, Method>();

	/**
	 * 本地方法ID缓存
	 */
	ArrayList<Integer> nativeMethodIndexs = new ArrayList<Integer>();
	
	/**
	 * 本地接口提供者
	 */
	NativeInterfaceProvider provider;
	
	/**
	 * 序列化上下文
	 */
	StreamContext context = new StreamContext();
	
	Hashtable<Object, Object> tagsMap = new Hashtable<Object,Object>();
	
	Hashtable<Class<?>, Object> implTable = new Hashtable<Class<?>, Object>();
	
	RemoteInvoker invoker;
	
	static ThreadLocal<Session> threadSessions = new ThreadLocal<Session>();
	
	public static void setCurrent(Session srmiSession) {
		threadSessions.set(srmiSession);
	}
	
	/***
	 * 获取当前线程上线问的Session，只在共享接口的本地实现方法里面才有效
	 * @param srmiSession
	 */
	public static Session getCurrent() {
		return threadSessions.get();
	}
	
	/***
	 * 一般由连接框架初始化
	 * @param provider
	 */
	public Session(NativeInterfaceProvider provider) {
		this.provider = provider;
	}
	
	/***
	 * 一般由连接框架初始化
	 * @param provider
	 */
	public void initInvoker(RemoteInvoker invoker) {
		this.invoker = invoker;
	}
	
	/**
	 * 获取一个程序自定义的Tag
	 * @param key
	 * @return
	 */
	public Object getTag(Object key) {
		return tagsMap.get(key);
	}
	
	/**
	 * 绑定一个程序自定义的Tag
	 * @param key
	 * @return
	 */
	public Object setTag(Object key, Object value) {
		return tagsMap.put(key, value);
	}
	
	public RemoteInvoker getInvoker() {
		return invoker;
	}
	
	public Object findImplement(Method method) throws InstantiationException {
		Class<?> clazz = method.getDeclaringClass();
		Object result = implTable.get(clazz);
		if(result == null) {
			throw new InstantiationException("没有找到接口" + clazz + "对应的实现");
		}
		return result;
	}
	
	/**
	 * 保存方法，如果方法已经存在则直接返回方法ID
	 * @param method
	 * @return
	 */
	public int saveRemoteMethod(Method method) {
		Object key = provider.getMethodKey(method);
		return remoteMethodTable.put(key, method);
	}
	
	public int getRemoteMethodId(String interfaceName, String method) {
		return remoteMethodTable.indexOfKey(provider.getMethodKey(interfaceName, method));
	}
	
	public Method getNativeMethod(String interfaceName, String methodName) {
		return provider.getMethod(provider.getMethodKey(interfaceName, methodName));
	}
	
	public Method getNativeMethod(int index) {
		return provider.getMethodAt(nativeMethodIndexs.get(index));
	}
	
	public int setNativeMethod(String interfaceName, String methodName) {
		int index = provider.getMethodIndex(interfaceName, methodName);
		nativeMethodIndexs.add(index);
		return nativeMethodIndexs.size() - 1;
	}
	
	public int saveNativeMethod(String interfaceName, String methodName) {
		int index = provider.getMethodIndex(interfaceName, methodName);
		nativeMethodIndexs.add(index);
		return nativeMethodIndexs.size()-1;
	}
	
	/**
	 * 获取共享接口的远程代理实现对象
	 * @param clazz
	 * @return
	 */
	public <T> T getImpl(Class<T> clazz) {
		return invoker.getImplement(clazz);
	}
	
	/**
	 * 注册所有obj实现的接口
	 * @param obj
	 */
	public void registerAll(Object obj) {
		Class<?> clazz = obj.getClass();
		Class<?> interfaces[] = clazz.getInterfaces();
		assert interfaces.length > 0 : "类型" + clazz + "没有实现任何接口...";
		for(int i = 0; i < interfaces.length; ++i) {
			register((Class)interfaces[i], obj);
		}
	}
	
	/**
	 * 给接口绑定具体的本地实现，以供远程调用
	 * @param interfaceClass
	 * @param implementClass
	 */
	public <I, C extends I> void register(Class<I> interfaceClass, C implement) {
		if(implTable.put(interfaceClass, implement) == null) {
			provider.addInterface(interfaceClass);
		}
	}
	
	/**
	 * 解除所有注册
	 * @param clazz
	 */
	public void deregisterAll(Class<?> clazz) {
		Class<?> interfaces[] = clazz.getInterfaces();
		for(int i = 0; i < interfaces.length; ++i) {
			deregister(interfaces[i]);
		}
	}
	
	/**
	 * 解除注册
	 * @param interfaceClass
	 */
	public void deregister(Class<?> interfaceClass) {
		implTable.remove(interfaceClass);
	}
}